package cn.xzc.job.type;

import cn.xzc.job.TimerContext;
import cn.xzc.job.Trigger;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 周期执行的触发器：周期时间间隔+触发器
 *
 * @author xzc
 * 当前时间 2024-02-12 21:30:00
 */
public class PeriodicTrigger implements Trigger {

    private final long period;

    private final TimeUnit timeUnit;

    private volatile long initialDelay;

    private volatile boolean fixedRate;


    /**
     * Create a trigger with the given period in milliseconds.
     */
    public PeriodicTrigger(long period) {
        this(period, null);
    }

    /**
     * Create a trigger with the given period and time unit. The time unit will
     * apply not only to the period but also to any 'initialDelay' value, if
     * configured on this Trigger later via {@link #setInitialDelay(long)}.
     */
    public PeriodicTrigger(long period,
                           
                           TimeUnit timeUnit) {
        this.timeUnit = (timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS);
        this.period = this.timeUnit.toMillis(period);
    }


    /**
     * Return this trigger's period.
     */
    public long getPeriod() {
        return this.period;
    }

    /**
     * Return this trigger's time unit (milliseconds by default).
     */
    public TimeUnit getTimeUnit() {
        return this.timeUnit;
    }

    /**
     * Return the initial delay, or 0 if none.
     */
    public long getInitialDelay() {
        return this.initialDelay;
    }

    /**
     * Specify the delay for the initial execution. It will be evaluated in
     * terms of this trigger's {@link TimeUnit}. If no time unit was explicitly
     * provided upon instantiation, the default is milliseconds.
     */
    public void setInitialDelay(long initialDelay) {
        this.initialDelay = this.timeUnit.toMillis(initialDelay);
    }

    /**
     * Return whether this trigger uses fixed rate ({@code true}) or
     * fixed delay ({@code false}) behavior.
     */
    public boolean isFixedRate() {
        return this.fixedRate;
    }

    /**
     * Specify whether the periodic interval should be measured between the
     * scheduled start times rather than between actual completion times.
     * The latter, "fixed delay" behavior, is the default.
     */
    public void setFixedRate(boolean fixedRate) {
        this.fixedRate = fixedRate;
    }

    /**
     * Returns the time after which a task should run again.
     * 周期性执行任务的触发器：下一个任务的执行时间
     */
    @Override
    public Date nextExecutionTime(TimerContext timerContext) {
        Date lastExecution = timerContext.lastScheduledExecutionTime();
        Date lastCompletion = timerContext.lastCompletionTime();
        //第一次执行任务时，直接获取当前时间
        if (lastExecution == null || lastCompletion == null) {
            return new Date(timerContext.getClock().millis() + this.initialDelay);
        }
        return this.fixedRate ? new Date(lastExecution.getTime() + this.period) : new Date(lastCompletion.getTime() + this.period);
    }


    @Override
    public boolean equals(
                          Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof PeriodicTrigger)) {
            return false;
        }
        PeriodicTrigger otherTrigger = (PeriodicTrigger) other;
        return (this.fixedRate == otherTrigger.fixedRate && this.initialDelay == otherTrigger.initialDelay &&
                this.period == otherTrigger.period);
    }

    @Override
    public int hashCode() {
        return (this.fixedRate ? 17 : 29) + (int) (37 * this.period) + (int) (41 * this.initialDelay);
    }

}